//
// Created by 郝羽 on 2018/3/21.
//

#ifndef RTC_XSWITCH_HINT_HPP
#define RTC_XSWITCH_HINT_HPP

#include <cstdint>
#include <string>
#include <netinet/in.h>
#include <map>
#include <iostream>
#include <srtp2/srtp.h>

#include <arpa/inet.h>
#include <sstream>

#include "packet.hpp"

class Decoder {
public:
    bool Decode(Packet* pkt) {
        srtp_err_status_t st;

        int len = pkt->pcapHeader_.caplen - pkt->rtpOffset_;
        int len_before = len;

        uint before_caplen = pkt->pcapHeader_.caplen;
        uint before_len = pkt->pcapHeader_.len;
        if (pkt->isRtcp_) {
            st = srtp_unprotect_rtcp(srtpSession_, pkt->capturedData_ + pkt->rtpOffset_, &len);
        } else {
            st = srtp_unprotect(srtpSession_, pkt->capturedData_ + pkt->rtpOffset_, &len);
        }
        if (st != srtp_err_status_ok) {
            fprintf(stderr, "could not decode packet: %s\n", to_string(st)) ;
            return false;
        }
        pkt->pcapHeader_.caplen = pkt->rtpOffset_ + len;
        pkt->pcapHeader_.len = pkt->rtpOffset_ + len;

        uint8_t* iplen_ptr = (pkt->capturedData_+pkt->ip_len_);
        uint16_t before_iplen = get2((const uint8_t *)iplen_ptr, 0);
        uint16_t iplen = before_iplen - (len_before - len);
        set2((uint8_t *)(iplen_ptr), 0, iplen);

        uint8_t* udplen_ptr = (pkt->capturedData_+pkt->udp_len_);
        uint16_t before_udplen = get2((const uint8_t *)udplen_ptr, 0);
        uint16_t udplen = before_udplen - (len_before - len);
        set2((uint8_t *)(udplen_ptr), 0, udplen);
        // fprintf(stderr, "brtp:artp:budp:audp:bip:aip:bcaplen:acaplen:blen:alen = %d:%d:%d:%d:%d:%d:%d:%d:%d:%d\n"
        //         , len_before, len, before_udplen, udplen, before_iplen, iplen, before_caplen, pkt->pcapHeader_.caplen, before_len, pkt->pcapHeader_.len) ;
        return true;
    }

    Decoder(const std::string& key) {
        if (key.size() != 60) {
            fprintf(stderr, "could not create decoder: key/salt length not 30 bytes\n");
            return;
        }
        key_ = new uint8_t[30];
        for (int i = 0; i < 30; i++) {
            key_[i] = std::stoi(key.substr(i*2, 2), nullptr, 16);
        }

        policy_.ssrc.type = ssrc_any_inbound;
        policy_.ssrc.value = 0;
        srtp_crypto_policy_set_rtp_default(&policy_.rtp);
        srtp_crypto_policy_set_rtcp_default(&policy_.rtcp);
        policy_.key = key_;
        policy_.window_size = 128;
        policy_.allow_repeat_tx = 0;
        policy_.ekt = nullptr;
        policy_.enc_xtn_hdr = nullptr;
        policy_.enc_xtn_hdr_count = 0;
        policy_.next = nullptr;
        auto status = srtp_create(&srtpSession_, &policy_);
        if (status != srtp_err_status_ok) {
            fprintf(stderr, "could not create decoder: %s\n", to_string(status));
        }
    }

    ~Decoder() {
        if (srtpSession_) {
            srtp_dealloc(srtpSession_);
            srtpSession_ = nullptr;
        }
        delete[] key_;
        key_ = nullptr;
    }
private:
    const char* to_string(srtp_err_status_t st) {
        switch(st) {
            case srtp_err_status_ok:           return "nothing to report";
            case srtp_err_status_fail:         return "unspecified failure";
            case srtp_err_status_bad_param:    return "unsupported parameter";
            case srtp_err_status_alloc_fail:   return "couldn't allocate memory";
            case srtp_err_status_dealloc_fail: return "couldn't deallocate properly";
            case srtp_err_status_init_fail:    return "couldn't initialize";
            case srtp_err_status_terminus:     return "can't process as much data as requested";
            case srtp_err_status_auth_fail:    return "authentication failure";
            case srtp_err_status_cipher_fail:  return "cipher failure";
            case srtp_err_status_replay_fail:  return "replay check failed (bad index)";
            case srtp_err_status_replay_old:   return "replay check failed (index too old)";
            case srtp_err_status_algo_fail:    return "algorithm failed test routine";
            case srtp_err_status_no_such_op:   return "unsupported operation";
            case srtp_err_status_no_ctx:       return "no appropriate context found";
            case srtp_err_status_cant_check:   return "unable to perform desired validation";
            case srtp_err_status_key_expired:  return "can't use key any more";
            case srtp_err_status_socket_err:   return "error in use of socket";
            case srtp_err_status_signal_err:   return "error in use POSIX signals";
            case srtp_err_status_nonce_bad:    return "nonce check failed";
            case srtp_err_status_read_fail:    return "couldn't read data";
            case srtp_err_status_write_fail:   return "couldn't write data";
            case srtp_err_status_parse_err:    return "error parsing data";
            case srtp_err_status_encode_err:   return "error encoding data";
            case srtp_err_status_semaphore_err: return "error while using semaphores";
            case srtp_err_status_pfkey_err:    return "error while using pfkey";
        }
        return "";
    }
private:
    uint8_t* key_;
    srtp_policy_t policy_;
    srtp_ctx_t *srtpSession_ = nullptr;


};
#endif //RTC_XSWITCH_HINT_HPP
